package cn.tj.wenda.cloud.web.filter;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import cn.tj.wenda.cloud.entity.ResponseBean;
import cn.tj.wenda.cloud.security.JsonWebToken;
import cn.tj.wenda.cloud.util.ConstUtil;
import io.netty.buffer.ByteBufAllocator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

//@Component
public class AuthSignatureFilter implements GlobalFilter, Ordered {
	
	static Logger logger = LoggerFactory.getLogger(AuthSignatureFilter.class);

	@Override
	public int getOrder() {
		return -200;
	}

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		logger.info("==================");
		logger.info("request = {}",JSONArray.toJSONString( exchange.getRequest()) );
		logger.info("resposne = {}",JSONArray.toJSONString( exchange.getResponse()) );
		
		ServerHttpRequest request = exchange.getRequest();
		ServerHttpResponse response = exchange.getResponse();
		
		String url = request.getPath().pathWithinApplication().value();
		HttpMethod httpMethod = request.getMethod();
		
	    logger.info("请求URL:"+url);
	    logger.info("method:" + httpMethod);
	    
		//处理登录
	    if(StringUtils.endsWith(url, "/login")) {
	    	if(StringUtils.equalsIgnoreCase("post", request.getMethodValue()) == false){//判断是否为POST请求
	    		String errMsg = "登录验证失败，使用了非POST进行了登录";
	    		logger.error(url + ":" + errMsg);
	    		return this.getReturnErrMsg(response,HttpStatus.UNAUTHORIZED,errMsg);
	    	}
	    	//请求的body
	        Flux<DataBuffer> body = request.getBody();
	        
	        AtomicReference<String> bodyRef = new AtomicReference<>();//缓存读取的request body信息
            body.subscribe(dataBuffer -> {
                CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());
                DataBufferUtils.release(dataBuffer);
                bodyRef.set(charBuffer.toString());
            });//读取request body到缓存
            
            String bodyStr = bodyRef.get();//获取request body
            if(StringUtils.isEmpty(bodyStr)) {
            	String errMsg = "登录验证失败，请输入账号或密码";
	    		logger.error(url + ":" + errMsg);
	    		return this.getReturnErrMsg(response,HttpStatus.UNAUTHORIZED,errMsg);
            }
            JSONObject obj = JSONObject.parseObject(bodyStr);
            if(!obj.containsKey("account") || !obj.containsKey("password")) {
            	String errMsg = "登录验证失败，请输入账号或密码";
	    		logger.error(url + ":" + errMsg);
	    		return this.getReturnErrMsg(response,HttpStatus.UNAUTHORIZED,errMsg);
            }
            String account = obj.getString("account").trim();
            String password = obj.getString("password").trim();
            if(StringUtils.isAnyEmpty(account,password)) {
            	String errMsg = "登录验证失败，账号或密码不能为空";
	    		logger.error(url + ":" + errMsg);
	    		return this.getReturnErrMsg(response,HttpStatus.UNAUTHORIZED,errMsg);
            }
            if(account.length() > 50 || password.length() > 50) {
            	String errMsg = "登录验证失败，账号或密码长度超出了50位";
	    		logger.error(url + ":" + errMsg);
	    		return this.getReturnErrMsg(response,HttpStatus.UNAUTHORIZED,errMsg);
            }
            
            DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
            Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
            request = new ServerHttpRequestDecorator(request){
            	@Override
            	public Flux<DataBuffer> getBody() {
            		return bodyFlux;
            	}
            };//封装我们的request

            
            return chain.filter(exchange.mutate().request(request).build());
	    	//return chain.filter(exchange);
	    }
	    
	    //处理token
		HttpHeaders headers = request.getHeaders();
        String token = headers.getFirst(ConstUtil.TOKEN);
        logger.info("token = {}",token);
        
        //从queryparam中取
        if(StringUtils.isEmpty(token)) {
        	MultiValueMap<String, String> map = request.getQueryParams();
        	if(map.containsKey("token")) {
        		token = map.getFirst("token");
        	}
        	else if(map.containsKey(ConstUtil.TOKEN)) {
        		token = map.getFirst(ConstUtil.TOKEN);
        	}
        }
        
        if(StringUtils.isEmpty(token)) {
        	String errMsg = "用户访问失败，请提供Token";
    		logger.error(url + ":" + errMsg);
    		return this.getReturnErrMsg(response,HttpStatus.UNAUTHORIZED,errMsg);
        }
        int checkVal = JsonWebToken.checkJWT(token);
        if(checkVal != 0) {
        	String errMsg = "用户访问失败，" + JsonWebToken.getCheckMsg(checkVal);
    		logger.error(url + ":" + errMsg);
    		return this.getReturnErrMsg(response,HttpStatus.UNAUTHORIZED,errMsg);
        }
        //修改header时使用
        //ServerWebExchange build = exchange.mutate().request(request.mutate().header(ConstUtil.TOKEN, token + "8889").build()).build();
        ServerWebExchange build = exchange;
        return chain.filter(build);
	}
	
	private Mono<Void> getReturnErrMsg(ServerHttpResponse resposne,HttpStatus httpStatus,String errMsg){
		resposne.setStatusCode(httpStatus);
		resposne.getHeaders().setContentType(MediaType.APPLICATION_JSON);
		ResponseBean res = new ResponseBean();
		res.setErr("1");
		res.setErrMsg(errMsg);
		
		byte[] bytes = JSONObject.toJSONBytes(res);
        DataBuffer buffer = resposne.bufferFactory().wrap(bytes);
        return resposne.writeWith(Flux.just(buffer));
	}
	
	protected DataBuffer stringBuffer(String value) {
		byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
		NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
		DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
		buffer.write(bytes);
		return buffer;
	}
}
